#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/ioctl.h>
#include <mtd/mtd-user.h>
#include <stdbool.h>

#define MS 1000

// Command: dev [char/block]
int main(int argc, char *argv[])
{
    char devname[20] = {0};
    char devtype[10] = {0};
    char cmd[10] = {0};
    char buff[100] = {0};
    const char test_buff[] = "abcd";
    unsigned int offset;
    unsigned int size;
    int ret;
    struct erase_info_user erase_info;
    struct mtd_info_user meminfo;
    bool isrunning = true;
    if (argc != 3) {
        printf("parameter should have device name[/dev/mtd] and device type[char/block], argc=%d\n", argc);
        return -1;
    }

    strncpy(devname, argv[1], strlen(argv[1]));
    strncpy(devtype, argv[2], strlen(argv[2]));
    int fd = open(devname, O_RDWR);
    if (fd == -1) {
        printf("open %s failed.err = %s\n", devname, strerror(errno));
        return -1;
    }

    while (isrunning) {
        if (!strncmp(devtype, "char", strlen("char"))) {
            printf("mtd char device!\nCommand:[read/write/erase/meminfo/quit/test]");
            scanf("%s", cmd);
            if (!strncmp(cmd, "write", strlen("write"))) {
                printf("offset:");
                scanf("%u", &offset);
                memset(buff, 0, 100);
                printf("write data:");
                scanf("%s", buff);

                erase_info.start = offset;
                erase_info.length = strlen(buff);                
                ret = ioctl(fd, MEMERASE, &erase_info);
                if (ret == -1) {
                    printf("memerase failed. %s\n", strerror(errno));
                    continue;
                }

                lseek(fd, offset, SEEK_SET);

                ret = write(fd, buff, erase_info.length);
                if (ret == -1) {
                    printf("write flash failed. %s\n", strerror(errno));
                }

            } else if (!strncmp(cmd, "read", strlen("read"))) {
                printf("offset:");
                scanf("%u", &offset);
                printf("size:");
                scanf("%u", &size);
                memset(buff, 0, 100);

                lseek(fd, offset, SEEK_SET);

                ret = read(fd, buff, size > 100 ? 100 : size);
                if (ret == -1) {
                    printf("read flash failed. %s\n", strerror(errno));
                } else {
                    printf("read data: %s\n", buff);
                }
            } else if (!strncmp(cmd, "erase", strlen("erase"))) {
                printf("offset:");
                scanf("%u", &offset);
                printf("size:");
                scanf("%u", &size);

                erase_info.start = offset;
                erase_info.length = size;
                ret = ioctl(fd, MEMERASE, &erase_info);
                if (ret == -1) {
                    printf("memerase failed. %s\n", strerror(errno));
                }
            } else if (!strncmp(cmd, "meminfo", strlen("meminfo"))) {
                ret = ioctl(fd, MEMGETINFO, &meminfo);
                if (ret == -1) {
                    printf("get meminfo failed. %s\n", strerror(errno));
                } else {                    
                    printf("meminfo:\nerasesize:%u\nflag:%u\n"
                        "oobsize:%u\nsize:%llu\ntype:%u\nwritesize:%u\n", meminfo.erasesize, meminfo.flags,
                    meminfo.oobsize, meminfo.size, meminfo.type, meminfo.writesize);
                }
            } else if (!strncmp(cmd, "test", strlen("test"))) {
                erase_info.start = 0;
                erase_info.length = 4096;
                ret = ioctl(fd, MEMERASE, &erase_info);
                if (ret == -1) {
                    printf("test erase mem failed. %s\n", strerror(errno));
                    continue;
                }
                usleep(MS);
                lseek(fd, 0, SEEK_SET);                
                ret = write(fd, test_buff, strlen(test_buff));
                if (ret == -1) {
                    printf("test case write failed. %s\n", strerror(errno));
                    continue;
                }
                usleep(MS);
                lseek(fd, 0, SEEK_SET);
                memset(buff, 0, 100);
                ret = read(fd, buff, strlen(test_buff));
                if (ret == -1) {
                    printf("test case read failed. %s\n", strerror(errno));
                    continue;
                }
                printf("test case:%s\n", buff);
            } else if (!strncmp(cmd, "quit", strlen("quit"))) {
                printf("quit\n");
                isrunning = false;
            } else {
                printf("unknown cmd %s\n", cmd);
            }
        } else {
            printf("mtd block device!\nCommand:[read/write]");
            scanf("%s", cmd);
            if (!strncmp(cmd, "write", strlen("write"))) {
                printf("offset:");
                scanf("%u", &offset);
                memset(buff, 0, 100);
                printf("write data:");
                scanf("%s", buff);

                ret = write(fd, buff, erase_info.length);
                if (ret == -1) {
                    printf("write flash failed. %s\n", strerror(errno));
                }
            } else if (!strncmp(cmd, "read", strlen("read"))) {
                printf("offset:");
                scanf("%u", &offset);
                printf("size:");
                scanf("%u", &size);
                memset(buff, 0, 100);

                ret = read(fd, buff, size > 100 ? 100 : size);
                if (ret == -1) {
                    printf("read flash failed. %s\n", strerror(errno));
                } else {
                    printf("read data: %s\n", buff);
                }
            } else if (!strncmp(cmd, "quit", strlen("quit"))) {
                printf("quit\n");
                isrunning = false;
            } else {
                printf("unknown cmd %s\n", cmd);
            }
        }
    }

    close(fd);
    return 0;
}